package com.file.parser;

import com.file.bo.BjSocialInsurance;
import com.file.bo.BjSocialInsuranceTran;
import com.file.bo.ResponseData;
import com.file.constant.ErrorCode;
import com.file.util.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import technology.tabula.*;
import technology.tabula.detectors.SpreadsheetDetectionAlgorithm;
import technology.tabula.extractors.BasicExtractionAlgorithm;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.*;

/**
 * @author anyspa
 */

@Slf4j
public class BjSocialInsurancePdfParser extends BasePdfParser {

    public ResponseData<String> parseBjSocialInsurancePdfToJson(String daId, String filePath) {
        log.info("parseBjSocialInsurancePdfToJson started, daId:{}", daId);
        String json = null;

        try {
            BjSocialInsurance socialInsurance = parseSocialInsurancePdf(filePath);
            json = JsonUtils.convertObjectToJson(socialInsurance);
        } catch (Exception e) {
            log.error("OnError|{}||{}|{}|{}|{}", ErrorCode.FILE_PARSE_EXCEPTION.getCode(), daId, "", "", "parseBjSocialInsurancePdfToJson failed", e);
            return new ResponseData<String>(json, ErrorCode.FILE_PARSE_EXCEPTION.getCode(), ErrorCode.FILE_PARSE_EXCEPTION.getMsg());
        }

        log.info("parseBjSocialInsurancePdfToJson completed, daId:{}", daId);
        return new ResponseData<String>(json, ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg());
    }

    public BjSocialInsurance parseSocialInsurancePdf(String filePath) {
        BjSocialInsurance bjSocialInsurance = parseSocialInsuranceHeader(filePath);

        parseDetails(bjSocialInsurance, filePath);

        return bjSocialInsurance;
    }

    private BjSocialInsurance parseSocialInsuranceHeader(String filePath) {
        BjSocialInsurance bjSocialInsurance = new BjSocialInsurance();
        String pdfHeaderText = getPdfTextByStripper2(filePath);

        String name = pdfHeaderText.substring(pdfHeaderText.indexOf("参保人姓名:") + 6, pdfHeaderText.indexOf("校验码:")).trim();
        String verifyCode = pdfHeaderText.substring(pdfHeaderText.indexOf("校验码:") + 4, pdfHeaderText.indexOf("社会保障号码:")).trim();
        String socialInsuranceNo = pdfHeaderText.substring(pdfHeaderText.indexOf("社会保障号码:") + 7, pdfHeaderText.indexOf("查询流水号:")).trim();
        String queryFlowingNo = pdfHeaderText.substring(pdfHeaderText.indexOf("查询流水号:") + 6, pdfHeaderText.indexOf("单位名称:")).trim();
        String companyName = pdfHeaderText.substring(pdfHeaderText.indexOf("单位名称:") + 5, pdfHeaderText.indexOf("查询日期:")).trim();
        String queryDate = pdfHeaderText.substring(pdfHeaderText.indexOf("查询日期:") + 5, pdfHeaderText.indexOf("一、养老保险单位变动记录")).trim();

        String pensionActualTotalPaymentYears = pdfHeaderText.substring(pdfHeaderText.indexOf("参保人在我市养老保险累计实际缴费年限") + 18,
                pdfHeaderText.indexOf("(其中含本市补填记录缴费年限")).trim();
        String medicalActualTotalPaymentYears = pdfHeaderText.substring(pdfHeaderText.indexOf("医疗保险累计实际缴费年限") + 12,
                pdfHeaderText.indexOf("截至")).trim();
        if (medicalActualTotalPaymentYears.contains("。")) {
            medicalActualTotalPaymentYears = medicalActualTotalPaymentYears.substring(0, medicalActualTotalPaymentYears.indexOf("。"));
        }
        String pensionPersonalAccountBalance = pdfHeaderText.substring(pdfHeaderText.indexOf("养老保险个人账户本息合计金额：") + 15,
                pdfHeaderText.indexOf("元。")).trim();

        bjSocialInsurance.setName(name);
        bjSocialInsurance.setVerifyCode(verifyCode);
        bjSocialInsurance.setSocialInsuranceNo(socialInsuranceNo);
        bjSocialInsurance.setQueryFlowingNo(queryFlowingNo);
        bjSocialInsurance.setCompanyName(companyName);
        bjSocialInsurance.setQueryDate(queryDate);
        bjSocialInsurance.setPensionActualTotalPaymentYears(pensionActualTotalPaymentYears);
        bjSocialInsurance.setMedicalActualTotalPaymentYears(medicalActualTotalPaymentYears);
        bjSocialInsurance.setPensionPersonalAccountBalance(pensionPersonalAccountBalance);
        return bjSocialInsurance;
    }

    private void parseDetails(BjSocialInsurance bjSocialInsurance, String filePath) {
        List<BjSocialInsuranceTran> bjSocialInsuranceTrans = new ArrayList<>();
        List<BjSocialInsurance.PensionUnitChangeRecord> pensionUnitChangeRecords = new ArrayList<>();

        // 1. 读取文件
        File pdf = new File(filePath);

        // 2. pdfbox读取PDDocument
        try (PDDocument pdfDocument = PDDocument.load(pdf)) {

            // 3. tabula新建ObjectExtractor和NurminenDetectionAlgorithm，同时准备接收表格Rectangle的结构
            ObjectExtractor objectExtractor = new ObjectExtractor(pdfDocument);
            // NurminenDetectionAlgorithm detectionAlgorithm = new NurminenDetectionAlgorithm();
            SpreadsheetDetectionAlgorithm sea = new SpreadsheetDetectionAlgorithm();

            Map<Integer, List<Rectangle>> detectedTables = new HashMap<>();

            // 4. 获取每页的PageIterator
            PageIterator pages = objectExtractor.extract();

            // 5. 解析每页的Rectangle(table的位置)
            while (pages.hasNext()) {
                Page page = pages.next();

                List<Rectangle> tablesOnPage = sea.detect(page);
                if (tablesOnPage.size() > 0) {
                    detectedTables.put(page.getPageNumber(), tablesOnPage);
                }
            }

            // 6.通过table位置获取表格具体内容
            // SpreadsheetExtractionAlgorithm bea = new SpreadsheetExtractionAlgorithm();
            BasicExtractionAlgorithm bea = new BasicExtractionAlgorithm();


            // 如果单页pdf中有多个表格，则解析出来的List<Rectangle>会有多个对象
            for (Map.Entry<Integer, List<Rectangle>> entry : detectedTables.entrySet()) {
                Page page = objectExtractor.extract(entry.getKey());

                // 第一页有两个表格: 养老保险单位变动记录 && 五险缴费明细
                if (page.getPageNumber() == 1) {
                    // 先解析养老保险单位变动记录
                    Rectangle rectangle = entry.getValue().get(0);
                    Page area = page.getArea(rectangle.getTop(), rectangle.getLeft(),rectangle.getBottom(), rectangle.getRight());

                    List<Table> table = bea.extract(area);
                    Table pensionUnitChangeRecordsTable = table.get(0);

                    // 解析第一页时，去掉表头第一行数据
                    for (int i = 0; i < pensionUnitChangeRecordsTable.getRowCount(); i++) {
                        if (Objects.nonNull(pensionUnitChangeRecordsTable.getCell(i,0).getText())) {
                            if (pensionUnitChangeRecordsTable.getCell(i,0).getText().contains("缴费起始年月")) {
                                continue;
                            }
                            BjSocialInsurance.PensionUnitChangeRecord pensionUnitChangeRecord =
                                    getPensionUnitChangeRecordsDetailsByTable(pensionUnitChangeRecordsTable, i);
                            pensionUnitChangeRecords.add(pensionUnitChangeRecord);
                        }
                    }
                    bjSocialInsurance.setPensionUnitChangeRecords(pensionUnitChangeRecords);

                    if (entry.getValue().size() == 2) {
                        // 解析五险缴费明细
                        rectangle = entry.getValue().get(1);
                        area = page.getArea(rectangle.getTop(), rectangle.getLeft(), rectangle.getBottom(), rectangle.getRight());

                        table = bea.extract(area);
                        Table socialInsuranceTranTable = table.get(0);
                        // 解析第一页时，去掉表头第一行数据
                        for (int i = 0; i < socialInsuranceTranTable.getRowCount(); i++) {
                            if (Objects.nonNull(socialInsuranceTranTable.getCell(i, 0).getText())) {
                                if (socialInsuranceTranTable.getCell(i, 2).getText().contains("养老实际缴")
                                        || socialInsuranceTranTable.getCell(i, 0).getText().contains("缴费起止年月")
                                        || socialInsuranceTranTable.getCell(i, 1).getText().contains("月数")) {
                                    continue;
                                }
                                BjSocialInsuranceTran socialInsuranceTran =
                                        getInsuranceTranDetailsByTable(socialInsuranceTranTable, i);
                                bjSocialInsuranceTrans.add(socialInsuranceTran);
                            }
                        }
                    }
                }

                // 第二页有一个表格:五险缴费明细
                if (page.getPageNumber() == 2) {
                    // 解析五险缴费明细
                    Rectangle rectangle = entry.getValue().get(0);
                    Page area = page.getArea(rectangle.getTop(), rectangle.getLeft(),rectangle.getBottom(), rectangle.getRight());

                    List<Table> table = bea.extract(area);
                    Table socialInsuranceTranTable = table.get(0);
                    // 解析第二页时，去掉表头第一行数据
                    for (int i = 0; i < socialInsuranceTranTable.getRowCount(); i++) {
                        if (Objects.nonNull(socialInsuranceTranTable.getCell(i,0).getText())) {
                            if (socialInsuranceTranTable.getCell(i, 2).getText().contains("养老实际缴")
                                    || socialInsuranceTranTable.getCell(i, 0).getText().contains("缴费起止年月")
                                    || socialInsuranceTranTable.getCell(i, 1).getText().contains("月数")) {
                                continue;
                            }
                            BjSocialInsuranceTran socialInsuranceTran =
                                    getInsuranceTranDetailsByTable(socialInsuranceTranTable, i);
                            bjSocialInsuranceTrans.add(socialInsuranceTran);
                        }
                    }
                }
            }

            bjSocialInsurance.setSocialInsuranceTranList(bjSocialInsuranceTrans);
        }  catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private BjSocialInsurance.PensionUnitChangeRecord getPensionUnitChangeRecordsDetailsByTable(Table t, int i) {
        BjSocialInsurance.PensionUnitChangeRecord pensionUnitChangeRecord = new BjSocialInsurance.PensionUnitChangeRecord();
        pensionUnitChangeRecord.setPaymentStartDate(t.getCell(i,0).getText());
        pensionUnitChangeRecord.setPaymentEndDate(t.getCell(i,1).getText());
        pensionUnitChangeRecord.setActualPaymentMonths(t.getCell(i,2).getText());
        pensionUnitChangeRecord.setCompanyName(t.getCell(i,3).getText());
        pensionUnitChangeRecord.setPaymentDistrict(t.getCell(i,4).getText());

        return pensionUnitChangeRecord;
    }

    // BasicExtractionAlgorithm
    private BjSocialInsuranceTran getInsuranceTranDetailsByTable(Table t, int i) {
        BjSocialInsuranceTran bjSocialInsuranceTran = new BjSocialInsuranceTran();
        bjSocialInsuranceTran.setPaymentStartAndEndDate(t.getCell(i,0).getText());

        BjSocialInsuranceTran.PensionActualPayment pensionActualPayment =
                new BjSocialInsuranceTran.PensionActualPayment();
        pensionActualPayment.setMonth(t.getCell(i,1).getText());
        // 237693 19015.44
        String text1 = t.getCell(i, 2).getText();
        if (StringUtils.isNotBlank(text1) && text1.contains(StringUtils.SPACE)) {
            String[] splits = text1.split(StringUtils.SPACE);
            if (splits.length == 2) {
                pensionActualPayment.setAnnualPaymentBase(splits[0]);
                pensionActualPayment.setPersonalPayment(splits[1]);
            }
        }
        bjSocialInsuranceTran.setPensionActualPayment(pensionActualPayment);

        BjSocialInsuranceTran.UnemploymentActualPayment unemploymentActualPayment =
                new BjSocialInsuranceTran.UnemploymentActualPayment();
        unemploymentActualPayment.setMonth(t.getCell(i,3).getText());
        String text2 = t.getCell(i, 4).getText();
        if (StringUtils.isNotBlank(text2) && text2.contains(StringUtils.SPACE)) {
            String[] splits = text2.split(StringUtils.SPACE);
            if (splits.length == 2) {
                unemploymentActualPayment.setAnnualPaymentBase(splits[0]);
                unemploymentActualPayment.setPersonalPayment(splits[1]);
            }
        }
        bjSocialInsuranceTran.setUnemploymentActualPayment(unemploymentActualPayment);

        BjSocialInsuranceTran.InjuryActualPayment injuryActualPayment =
                new BjSocialInsuranceTran.InjuryActualPayment();
        String text3 = t.getCell(i, 5).getText();
        if (StringUtils.isNotBlank(text3) && text3.contains(StringUtils.SPACE)) {
            String[] splits = text3.split(StringUtils.SPACE);
            if (splits.length == 2) {
                injuryActualPayment.setMonth(splits[0]);
                injuryActualPayment.setAnnualPaymentBase(splits[1]);
            }
        }
        bjSocialInsuranceTran.setInjuryActualPayment(injuryActualPayment);

        BjSocialInsuranceTran.MedicalActualPayment medicalActualPayment =
                new BjSocialInsuranceTran.MedicalActualPayment();
        medicalActualPayment.setMonth(t.getCell(i,6).getText());
        String text4 = t.getCell(i, 7).getText();
        if (StringUtils.isNotBlank(text4) && text4.contains(StringUtils.SPACE)) {
            String[] splits = text4.split(StringUtils.SPACE);
            if (splits.length == 2) {
                medicalActualPayment.setAnnualPaymentBase(splits[0]);
                medicalActualPayment.setPersonalPayment(splits[1]);
            }
        }
        bjSocialInsuranceTran.setMedicalActualPayment(medicalActualPayment);

        BjSocialInsuranceTran.FertilityActualPayment fertilityActualPayment =
                new BjSocialInsuranceTran.FertilityActualPayment();
        String text5 = t.getCell(i, 8).getText();
        if (StringUtils.isNotBlank(text5) && text5.contains(StringUtils.SPACE)) {
            String[] splits = text5.split(StringUtils.SPACE);
            if (splits.length == 2) {
                fertilityActualPayment.setMonth(splits[0]);
                fertilityActualPayment.setAnnualPaymentBase(splits[1]);
            }
        }
        bjSocialInsuranceTran.setFertilityActualPayment(fertilityActualPayment);

        return bjSocialInsuranceTran;
    }

    public static void main(String[] args) throws IOException {
        BjSocialInsurancePdfParser bjSocialInsurancePdfParser = new BjSocialInsurancePdfParser();
        BjSocialInsurance bjSocialInsurance = bjSocialInsurancePdfParser.parseSocialInsurancePdf("D:\\data\\files\\北京社保近5年数据.pdf");
        // BjSocialInsurance bjSocialInsurance = bjSocialInsurancePdfParser.parseSocialInsurancePdf("E:\\data\\files\\北京参保人员缴费信息.pdf");


        String json = JsonUtils.convertObjectToJson(bjSocialInsurance);
        log.info(json);
        File file = new File("D:\\data\\files\\北京参保人员缴费信息.json");
        OutputStream output = new FileOutputStream(file);
        IOUtils.write(json, output, Charset.forName("Unicode"));
    }
}
